Skip to content

Conversation

@CalamitousFelicitousness
Copy link

@CalamitousFelicitousness CalamitousFelicitousness commented Nov 29, 2025

What does this PR do?

Adds support for LoRA for ZImagePipeline and adds a conversion script into Diffusers format. Resolves issue #12745

Before submitting

Who can review?

Anyone in the community is free to review the PR once the tests have passed. Feel free to tag
members/contributors who may be interested in your PR.

@sayakpaul

@CalamitousFelicitousness
Copy link
Author

Tested using a simple script.

Testing script
#!/usr/bin/env python
"""Test script for ZImage LoRA support."""

import sys
sys.path.insert(0, '/home/ohiom/diffusers/src')

import torch
from diffusers import ZImagePipeline

# Paths
MODEL_PATH = "database/models/huggingface/models--Tongyi-MAI--Z-Image-Turbo/snapshots/78771b7e11b922c868dd766476bda1f4fc6bfc96"
LORA_PATH = "TechnicallyColorZ_V1.safetensors"

print("Loading ZImagePipeline...")
pipe = ZImagePipeline.from_pretrained(
    MODEL_PATH,
    torch_dtype=torch.bfloat16,
    local_files_only=True,
)
pipe.to("cuda")

print(f"Pipeline loaded. Has load_lora_weights: {hasattr(pipe, 'load_lora_weights')}")

print(f"\nLoading LoRA from {LORA_PATH}...")
pipe.load_lora_weights(LORA_PATH)
print("LoRA loaded successfully!")

# Generate an image
prompt = "t3chnic4lly vibrant 1960s close-up of a woman sitting under a tree in a blue skit and white blouse, she has blonde wavy short hair and a smile with green eyes lake scene by a garden with flowers in the foreground 1960s styl;e film She's holding her hand out there is a small smooth frog in her palm, she's making eye contact with the toad."
print(f"\nGenerating image with prompt: {prompt}")

image = pipe(
    prompt=prompt,
    num_inference_steps=8,
    guidance_scale=1.0,
    height=1024,
    width=1024,
    generator=torch.Generator(device="cuda").manual_seed(42),
).images[0]

output_path = "test_zimage_lora_output.png"
image.save(output_path)
print(f"\nImage saved to {output_path}")

@sayakpaul
Copy link
Member

@asomoza cc

@asomoza
Copy link
Member

asomoza commented Nov 30, 2025

thanks for the contribution, it works ok without scale but we need to add the transformer model here

With that:

without lora lora 1.0 lora 0.5
z_image_turbo_output_no_lora z_image_turbo_output_lora z_image_turbo_output_lora_05

Also I did a quick run on the tests to check if this was detected but they all fail with:

ValueError: too many values to unpack (expected 3)

Copy link
Member

@sayakpaul sayakpaul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What a clean PR this is! Thanks so much for contributing!

@HuggingFaceDocBuilderDev

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

- Override test_lora_fuse_nan to use ZImage's 'layers' attribute
  instead of 'transformer_blocks'
- Skip block-level LoRA scaling test (not supported in ZImage)
- Add required imports: numpy, torch_device, check_if_lora_correctly_set
@CalamitousFelicitousness
Copy link
Author

CalamitousFelicitousness commented Nov 30, 2025

@sayakpaul You are too kind.
@asomoza Thank you so much for your review, and apologies for my mistake, ironically, in the script testing the testing scripts. Now I have updated peft.py and fixes all the errors I can manage.

As for the remaining failures, as far as I can tell, due to ZImage requiring torch.use_deterministic_algorithms(False) those tests cannot be completed. Please advise if I am correct, and those tests can be skipped, or are there any remaining steps for me to do.
From my understanding the changes needed to make those tests pass, like using a single pipeline(?), would be rather intensive, so I'm afraid I will have to defer to your judgement.

@asomoza
Copy link
Member

asomoza commented Nov 30, 2025

thanks a lot @CalamitousFelicitousness, can you please add the ZImageLoraLoaderMixin to the lora docs here

for the tests, I leave it to @sayakpaul to make the decision

Copy link
Member

@sayakpaul sayakpaul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go!

@sayakpaul
Copy link
Member

Hmm, the failing tests -- are these because of the numerical instabilities arising from the use of torch.empty within the Z-Image DiT?

ZImage uses 'attention.to_k' naming convention instead of 'attn.to_k',
so the base test's module name search loop never finds a match. This
override uses the correct naming pattern for ZImage architecture.
@CalamitousFelicitousness
Copy link
Author

CalamitousFelicitousness commented Dec 1, 2025

Squashed one more naming convention issue with an override.

My best somewhat-educated 2AM guess is that it lies somewhere between the Complex64 RoPE and torch.empty, unfortunately here I am beginning to approach the functional limits of my practical knowledge, so if anything comes to mind later on, I will bring it up.

@sayakpaul
Copy link
Member

@CalamitousFelicitousness that is understandable. I am facing something similar in #12741

@sayakpaul
Copy link
Member

Let's try to add a is_flaky decorator to the LoRA tester class, @CalamitousFelicitousness and see if that helps.

@CalamitousFelicitousness
Copy link
Author

CalamitousFelicitousness commented Dec 1, 2025

They are really flaky. I'm doing 10 retries right now, and it whittled down the failures to 6 at the lowest, some tests are failing one run and pass on another. I will just set it to 100 and see if they pass.

100 retries
==================================================================================================================================== warnings summary ====================================================================================================================================
tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_get_adapters
tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_get_list_adapters
tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_simple_inference_with_text_denoiser_multi_adapter
tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_simple_inference_with_text_denoiser_multi_adapter_delete_adapter
tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_simple_inference_with_text_denoiser_multi_adapter_weighted
tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_simple_inference_with_text_lora_denoiser_fused_multi
  /home/ohiom/diffusers/venv/lib/python3.13/site-packages/peft/tuners/tuners_utils.py:282: UserWarning: Already found a `peft_config` attribute in the model. This will lead to having multiple adapters in the model. Make sure to know what you are doing!
    warnings.warn(

tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_lora_B_bias
  /home/ohiom/diffusers/venv/lib/python3.13/site-packages/peft/tuners/lora/layer.py:170: PeftWarning: `lora_bias=True` was passed but the targeted layer of type Linear has no bias. This means that merging LoRA weights won't be possible.
    warnings.warn(

tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_lora_fuse_nan
  /home/ohiom/diffusers/venv/lib/python3.13/site-packages/peft/tuners/tuners_utils.py:1817: UserWarning: All adapters are already merged, nothing to do.
    warnings.warn("All adapters are already merged, nothing to do.")

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================================================================================================ short test summary info =================================================================================================================================
FAILED tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_logs_info_when_no_lora_keys_found - AssertionError: False is not true
FAILED tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_lora_scale_kwargs_match_fusion - AssertionError: False is not true : Fused lora should not change the output
FAILED tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_set_adapters_match_attention_kwargs - AssertionError: False is not true : Lora + scale should match the output of `set_adapters()`.
FAILED tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_simple_inference_with_text_denoiser_lora_and_scale - AssertionError: False is not true : Lora + scale should change the output
FAILED tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_simple_inference_with_text_denoiser_multi_adapter - AssertionError: False is not true : output with no lora and output with lora disabled should give same results
=========================================================================================================== 5 failed, 36 passed, 10 skipped, 8 warnings in 1253.64s (0:20:53) ============================================================================================================
Another 100 retries
================================================================================================================================ short test summary info =================================================================================================================================
FAILED tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_lora_scale_kwargs_match_fusion - AssertionError: False is not true : Fused lora should not change the output
FAILED tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_set_adapters_match_attention_kwargs - AssertionError: False is not true : Lora + scale should match the output of `set_adapters()`.
FAILED tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_simple_inference_with_text_denoiser_lora_and_scale - AssertionError: False is not true : Lora + scale should change the output
FAILED tests/lora/test_lora_layers_z_image.py::ZImageLoRATests::test_simple_inference_with_text_lora_unloaded - AssertionError: False is not true : Fused lora should change the output
=========================================================================================================== 4 failed, 37 passed, 10 skipped, 8 warnings in 2019.36s (0:33:39) ============================================================================================================

Status: We have three remaining tests that haven't passed yet:

  1. test_lora_scale_kwargs_match_fusion
  2. test_set_adapters_match_attention_kwargs
  3. test_simple_inference_with_text_denoiser_lora_and_scale

@CalamitousFelicitousness
Copy link
Author

@sayakpaul The last three tests fail, even at 250 retries, I tried to initialise the padding tokens to try and get them to pass, but no dice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants